import java.awt.geom.Ellipse2D
import java.io.PrintWriter
import java.{beans, io}

class Sheet10 {

}

trait RectangleLike1 { // 能够利用Ellipse2D构建Rectangle
  val rectangle: java.awt.Rectangle = getBounds

  def grow(h: Int, v: Int): Unit = {
    rectangle.grow(h, v)
    setFrame(rectangle.x, rectangle.y, rectangle.width, rectangle.height)
  }

  def translate(dx: Int, dy: Int): Unit = {
    rectangle.translate(dx, dy)
    setFrame(rectangle.x, rectangle.y, rectangle.width, rectangle.height)
  }

  def getBounds: java.awt.Rectangle

  def getX: Double

  def getY: Double

  def getWidth: Double

  def getHeight: Double

  def setFrame(x: Double, y: Double, w: Double, h: Double): Unit

  override def toString = s"[$getX, $getY, $getWidth, $getHeight]"
}

trait RectangleLike2 { // 能够利用Ellipse2D构建Rectangle
  def getX: Double

  def getY: Double

  def getWidth: Double

  def getHeight: Double

  def setFrame(x: Double, y: Double, width: Double, height: Double)

  def translate(dx: Double, dy: Double): Unit = {
    setFrame(getX + dx, getY + dy, getWidth, getHeight)
  }

  def grow(dx: Double, dy: Double): Unit = {
    setFrame(getX - dx, getY - dy, getWidth + 2 * dx, getHeight + 2 * dy)
  }

  override def toString = s"[$getX, $getY, $getWidth, $getHeight]"
}

class OrderedPoint extends java.awt.Point with scala.math.Ordered[java.awt.Point] {
  override def compare(that: java.awt.Point): Int = {
    val compare = this.x - that.x
    if (compare == 0) {
      this.y - that.y
    } else {
      compare
    }
  }
}

trait Logger {
  def log(message: String): Unit
}

trait DummyLogger extends Logger {
  override def log(message: String): Unit = {}
}

trait ConsoleLogger extends Logger {
  override def log(message: String): Unit = println(message)
}

trait FileLogger extends Logger {
  val out = new PrintWriter("app.log")
  out.println(s"#${java.time.Instant.now()}")

  override def log(message: String): Unit = {
    out.println(message)
    out.flush()
  }
}

trait CryptoLogger extends Logger {
  val key = 3

  abstract override def log(message: String): Unit = {
    super.log(Sheet10.encrypt(message, key))
  }
}

trait PropertyChangeSupportLike {
  val support: beans.PropertyChangeSupport = new beans.PropertyChangeSupport(this)

  def addPropertyChangeListener(listener: beans.PropertyChangeListener): Unit = {
    support.addPropertyChangeListener(listener)
  }

  def removePropertyChangeListener(listener: beans.PropertyChangeListener): Unit = {
    support.removePropertyChangeListener(listener)
  }

  def firePropertyChange(propertyName: String, oldValue: AnyRef, newValue: AnyRef): Unit = {
    support.firePropertyChange(propertyName, oldValue, newValue)
  }

  def firePropertyChange(propertyName: String, oldValue: Int, newValue: Int): Unit = {
    support.firePropertyChange(propertyName, oldValue, newValue)
  }

  def firePropertyChange(propertyName: String, oldValue: Boolean, newValue: Boolean): Unit = {
    support.firePropertyChange(propertyName, oldValue, newValue)
  }

  // ... class 变 trait, so you can extends another class now
}

class BeansPoint(x: Int, y: Int) extends java.awt.Point(x, y) with PropertyChangeSupportLike {
  def this() = this(0, 0)
}

trait Buffering {
  this: io.InputStream =>
  val BUF_SIZE: Int = 5
  private val buf = new Array[Byte](BUF_SIZE)
  private var total: Int = 0
  private var pos: Int = 0

  override def read(): Int = {
    if (pos >= total) {
      total = this.read(buf, 0, BUF_SIZE)
      if (total < 0) return -1
      pos = 0
    }
    pos += 1
    buf(pos - 1)
  }
}

object Sheet10 extends App {

  // Quest1
  val egg1 = new Ellipse2D.Double(5, 10, 20, 30) with RectangleLike1
  println(egg1)
  egg1.translate(10, -10)
  println(egg1)
  egg1.grow(10, 20)
  println(egg1)

  println("----")

  val egg2 = new Ellipse2D.Double(5, 10, 20, 30) with RectangleLike2
  println(egg2)
  egg2.translate(10, -10)
  println(egg2)
  egg2.grow(10, 20)
  println(egg2)

  // Quest2 OrderedPoint

  // Quest3 lin(BitSet) =
  /*

  lin(BitSetLike)
  = BitSetLike >> lin(SortedSetLike)
  = BitSetLike >> (SortedSetLike >> lin(SetLike) >> lin(Sorted))
  = BitSetLike >> SortedSetLike >> (SetLike >> lin(Parallelizable) >>
   lin(Subtractable) >> lin(GenSetLike) >> lin(IterableLike))
  ... too many
  lin(BitSet)
  = BitSet >> lin(Serializable) >> lin(BitSetLike) >>
  lin(scala.collection.BitSet) >> lin(SortedSet) >>
  lin(scala.collection.AbstractSet)
  = BitSet >> Serializable >>
  (BitSetLike >> lin(SortedSetLike))
  */

  // Quest4
  val logger = new {
    override val key = 3
  } with FileLogger with CryptoLogger
  logger.log("Hello World")

  // Quest9
  val input = new io.FileInputStream("src/input.txt") with Buffering
  for (_ <- 1 to 11) print(input.read() + " ")

  def decrypt(str: String, k: Int): String = {
    str.map(x => shift(x, 0 - k)).mkString
  }

  def encrypt(str: String, k: Int): String = {
    str.map(x => shift(x, k)).mkString
  }

  def shift(char: Char, k: Int): Char = {
    var c = char
    // 如果字符串中的某个字符是小写字母
    if (c >= 'a' && c <= 'z') {
      c = c + k % 26 toChar; // 移动key%26位
      if (c < 'a') {
        c = c + 26 toChar; // 向左超界
      } else if (c > 'z') {
        c = c - 26 toChar; // 向右超界
      }
    }
    // 如果字符串中的某个字符是大写字母
    else if (c >= 'A' && c <= 'Z') {
      c = c + k % 26 toChar; // 移动key%26位
      if (c < 'A') {
        c = c + 26 toChar; // 同上
      } else if (c > 'Z') {
        c = c - 26 toChar; // 同上
      }
    }
    c
  }
}